module executor.message.UnrecevieMsgExecutor;

import msgtrans;
import message.Constants;
import message.MsgDefine;
import google.protobuf;
import hunt.logging;
import std.array;
import std.conv : to;
import hunt.util.Serialize;
import client.GateServerClient;
import client.MsgServerClient;
import service.OfflineService;
// import hunt.httpclient;
import requests;
import app.config.CustomConfig;
import repository.UserRepository;
import model.User;
import repository.FriendRepository;
import model;
import repository.GroupRepository;
import repository.DeviceTokenRepository;
import message.Gpush;
import core.thread;
import std.parallelism;
import comm.QueueJob;
import std.digest.sha;
import hunt.util.DateTime;
import core.sync.mutex;
import std.string;
import botan.hash.sha2_32 : SHA256;
import std.stdio;
import std.digest.sha : sha256Of;
import memutils.unique;
import botan.libstate.lookup;
import std.json;
import dackson;

enum string appid = "Sost6uQPmR6An2S7lTg9l2";
enum string appKey = "GsiK4tlqrR67YkY0zWsbMA";
enum string masterSecret = "xS9HyFGIkC6U0CwAGgnAR6";
enum string pushServer = "https://restapi.getui.com/v2";

    // "push_channel":{
    //     "android":{
    //         "ups":{
    //             "notification":{
    //                 "title":"请填写android标题",
    //                 "body":"请填写android内容",
    //                 "click_type":"url",
    //                 "url":"https://xxx"
    //             }
    //         }
    //     }
    // }
struct GetuiPushData {
    string request_id;

    struct Settings {
        uint tts;
    }

    Settings settings;

    struct Audience {
        string[] cid;
    } 

    Audience audience;

    struct PushChannel {
        struct Android {
            struct Ups {
                struct Notification
                {
                    string title = "私信";
                    string body = "您有新的私信消息";
                    string click_type = "url";
                    string url = "https://account.openchat.mobi";
                }
                Notification notification;
            }
            Ups ups;
        }
        Android android;

        struct iOS {
            string type = "notify";
            string payload = "iOS自定义消息";
            struct Aps {
                struct Alert {
                    string title = "私信";
                    string body = "您有新的私信消息";
                }
                Alert alert;
                @JsonProperty("content-available")
                uint content_available = 0;
            }
            Aps aps;
            string auto_badge = "+1";
        }
        iOS ios;
    }

    PushChannel push_channel;

    struct PushMessage {
        struct Notification {
            string title;
            string body;
            string click_type;
            string url;
        }

        Notification notification;
    }

    PushMessage push_message;
}

struct GetuiTokenData {
    string sign;
    string timestamp;
    string appkey;
}

struct GetuiTokenResult {
    uint code = 0;
    string msg = "";

    Data data;

    struct Data {
        string expire_time = "";
        string token = "";
    }
}

private __gshared string _getuiToken = "";
private __gshared ulong _getuiExpireTime = 0;

string getGetuiToken() {
    
    Mutex mtx = new Mutex;

    mtx.lock();
    scope(exit) {
        mtx.unlock();
    }

    long ms = DateTime.currentTimeMillis();

    if (_getuiToken.length > 0 && _getuiExpireTime > 0 && ms < _getuiExpireTime)
    {
        return _getuiToken;
    }

    GetuiTokenData tokenData;
    tokenData.appkey = appKey;

    string timestamp = ms.to!string;
    tokenData.timestamp = timestamp;

    // for botan:
    auto sha = retrieveHash("SHA-256").clone();
	sha.update(appKey ~ timestamp ~ masterSecret);

    tokenData.sign = sha.finished()[].toHexString!(LetterCase.lower);

    logInfof("Getui access data: %s", tokenData.toJson);

    string uri = pushServer ~ "/" ~ appid ~ "/auth";

    Request req = Request();
    Response res = req.post(uri, toJson(tokenData).toString().dup, "application/json;charset=utf-8");

    if (res.code == 200)
    {
        string content = cast(string)res.responseBody.data[][0..res.responseBody.length];
        logInfof("Getui token rs: %s", content);

        // https://docs.getui.com/getui/server/rest_v2/token/
        GetuiTokenResult result;
        try {
            result = content.decodeJson!GetuiTokenResult;
        } catch(Throwable e) {
            logErrorf("json parse error: %s", e.msg);
        }

        if (result.code > 0)
        {
            logErrorf("Getui token result error code: %d, msg: %s", result.code, result);
            return null;
        }

        logInfof("Getui token: %s", result.data.token);

        // 设置新的 getui token 并且刷新过期时间
        _getuiToken = result.data.token;
        _getuiExpireTime = result.data.expire_time.to!ulong;
    } else {
        string content = cast(string)res.responseBody.data[][0..res.responseBody.length];
        logError("Get token error: %s", content);
        _getuiToken = "";
    }
    
    // Response response = Http.asJson().post(pushServer ~ "/auth", toJson(tokenData));
    // if (response.isOk)
    // {
    //     // 
    //     auto content = response.content();

    //     // 解析返回值
    //     // https://docs.getui.com/getui/server/rest_v2/token/
    //     JSONValue j = content.toJson;
    //     GetuiTokenResult result = j.toObject!GetuiTokenResult;
    //     if (result.code > 0)
    //     {
    //         logErrorf("Getui token result error code: %d, msg: %s", result.code, result.msg);
    //         return null;
    //     }

    //     logInfof("Getui token: %s", result.data.token);

    //     // 设置新的 getui token 并且刷新过期时间
    //     _getuiToken = result.data.token;
    //     _getuiExpireTime = result.data.expire_time.to!ulong;
    // } else {
    //     logError("Get token error: %s", response.content);
    //     _getuiToken = "";
    // }

    return _getuiToken;
}

void publishUnreceivedMessageRES(Msg msg)
{
    // 个推
    string pushUri = pushServer ~ "/" ~ appid ~ "/push/single/cid";

    DeviceTokenRepository tokenRepo = new DeviceTokenRepository;
    auto tokens = tokenRepo.findToken(to!uint(msg.localId));
    logInfof("localId:%s  ------", msg.localId );

    GetuiPushData pushData;

    pushData.request_id = "0000000001";
    pushData.settings.tts = 7200000;

    foreach (DeviceToken token; tokens)
    {
        pushData.audience.cid ~= token.token;
    }

    if (pushData.audience.cid.length == 0)
    {
        logWarningf("Not found one device token.");
        return;
    }

    pushData.push_message.notification.title = "您有新消息";
    pushData.push_message.notification.body = "点击查看详情";
    pushData.push_message.notification.click_type = "url";
    pushData.push_message.notification.url = "https://account.openchat.mobi";

    logInfof("Push uri: %s", pushUri);
    logInfof("Push content: %s", pushData.toJson.toString);

    string[string] headers;
    headers["token"] = getGetuiToken();
    logInfof("Getui token: %s", headers["token"]);

    Request req = Request();
    req.addHeaders(headers);
    Response res = req.post(pushUri, toJson(pushData).toString.dup, "application/json;charset=utf-8");

    if (res.code == 200)
    {
        string content = cast(string)res.responseBody.data[][0..res.responseBody.length];
        logInfof("Getui push OK: %s", content);
    }
    else
    {
        string content = cast(string)res.responseBody.data[][0..res.responseBody.length];
        logErrorf("Getui push error: %s", content);
    }

    // auto response = Http.withHeaders(headers).post(pushUri, pushData.toJson);

    // if (response.isOk())
    // {
    //     logInfof("Push successed: %s", response.content());
    // }
    // else
    // {
    //     logError("Push error: %s", response.content);
    // }

  // import message.Gpush;
  // import hunt.serialization.JsonSerializer;
  // UserRepository userRepo = new UserRepository;
  // FriendRepository friendRepo = new FriendRepository;
  // GroupRepository  groupRepo = new GroupRepository;
  // DeviceTokenRepository tokensRepo = new DeviceTokenRepository;
  // string title;
  // Friend friend =  friendRepo.findByChatId(to!int(msg.chatId),to!int(msg.localId));
  // auto usr =  userRepo.findAllByUID(msg.localId);
  // if(friend !is null)
  // {
  //     if(friend.aliasName !is null)
  //     {
  //         title = friend.aliasName;
  //     }else
  //     {
  //         title = userRepo.findAllByUID(to!string(friend.friendId)).nickname;
  //     }
  // }else
  // {
  //    ChatGroup group = groupRepo.findByUid(to!int(msg.chatId));
  //    if(group !is null)
  //    {
  //       title = group.aliasName;
  //    }
  // }

  // auto tokens = tokensRepo.findToken(to!uint(msg.localId));
  // logInfof("localId:%s  ------", msg.localId );

  // foreach (DeviceToken token; tokens)
  // {
  //   logInfof("token:%s  ------", token.token );
  //   Notification notification;
  //   notification.title = title;
  //   notification.image = "http://test.com/test.png";
  //   switch (msg.type)
  //   {
  //     case MsgType.STRING:
  //     {
  //       notification.msgBody = msg.message;
  //       break;
  //     }
  //     case MsgType.PICTURE:
  //     {
  //       notification.msgBody = "[图片]";
  //       break;
  //     }
  //     case MsgType.FILE:
  //     {
  //       notification.msgBody = "[文件]";
  //       break;
  //     }
  //     case MsgType.VOICE:
  //     {
  //       notification.msgBody = "[语音]";
  //       break;
  //     }
  //     case MsgType.VIDEO:
  //     {
  //       notification.msgBody = "[视频]";
  //       break;
  //     }
  //     case MsgType.RED_ENVELOPE:
  //     {
  //       notification.msgBody = "[红包]";
  //       break;
  //     }
  //     default:
  //     break;
  //   }

  //   if (notification.msgBody.length == 0)
  //   {
  //     return;
  //   }

  //   Gdate gdate;

  //   Content content;
  //   content.chat_id = msg.chatId;
  //   content.message_id = msg.messageId;

  //   gdate.content = content;
  //   gdate.type = 1;

  //   Apns apns;
  //   apns.badge = usr.unread;

  //   Gpush gpush;
  //   gpush.push_token = token.token;
  //   gpush.notification = notification;
  //   gpush.data = gdate;
  //   gpush.apns = apns;


  //   auto json = toJson(gpush);
  //   string[string] headers;
  //   headers["appkey"]="test";
  //   try {
  //     logInfof("begin before .. to %s .. %s",serverConfig.gpush, json.toString());
  //     auto response = Http.withHeaders(headers).post(serverConfig.gpush,"application/json",cast(ubyte[])json.toString());
  //     //Response response = Http.asJson().post("http://api-dpush.test.ptevent.cn/v1/push",json);
  //     logInfof("begin push .. to %s .. %s",serverConfig.gpush, json.toString());
  //     if(response.isOk())
  //     {
  //       logInfof("gpush: %s" ,response.content);
  //     }else
  //     {
  //       logError("gpush error %s",response.content);
  //     }
  //   }catch(Throwable e)
  //   {
  //     logErrorf("error %s",e);
  //   }

  //   //try{
  //   //  Response res = Http.asJson().post("http://api-dpush.test.ptevent.cn/v1/push",json);
  //   //}catch(Throwable e)
  //   //{
  //   //  logErrorf("error %s",e);
  //   //}


  //   //string[string] headers;
  //   ////headers["Content-Type"] = "application/json";
  //   //try {
  //   //  logInfof("begin before .. to %s .. %s",serverConfig.gpush, json.toString());
  //   //  auto response = Http.withHeaders(headers).post(serverConfig.gpush,"application/json",cast(ubyte[])json.toString());
  //   //  logInfof("begin push .. to %s .. %s",serverConfig.gpush, json.toString());
  //   //  if(response.isOk())
  //   //  {
  //   //    logInfof("gpush: %s" ,response.content);
  //   //  }else
  //   //  {
  //   //    logError("gpush error %s",response.content);
  //   //  }
  //   //}catch(Throwable e)
  //   //{
  //   //  logErrorf("error %s",e);
  //   //}
  // }
}

/**
离线消息模块
**/

@TransportServer(LogicServerName)
class UnrecevieMsgExecutor : AbstractExecutor!(UnrecevieMsgExecutor) {

  //获取离线消息接口
  @MessageId(MESSAGE.CMD_UNRECEIVE_REQ)
  void unrecevie(TransportContext ctx, MessageBuffer buffer) {

    auto req = new openReceivedMessageREQ();
    buffer.data.fromProtobuf!openReceivedMessageREQ(req);

    ulong realId = (req.clientId >> 16);
    UnreceivedMessageRES res  = OfflineService.processOffline(realId);
    res.clientId =req.clientId;
    ctx.session.send(new MessageBuffer(MESSAGE.CMD_UNRECEIVE_RES,res.toProtobuf.array));
    logInfo("OfflineExecutor ----- ");
  }

  //插入离线消息接口
  @MessageId(MESSAGE.CMD_PUT_UNRECEIVE)
  void putUnreceive(TransportContext ctx, MessageBuffer buffer) {
    auto req = new Msg();
    logInfo("CMD_PUT_UNRECEIVE ----- ");
    buffer.data.fromProtobuf!Msg(req);
    OfflineService.processPutOffline(req);
    logInfo ("-------------------------------------------");
    QueueJob.getInstance.pushJob(req);
    // auto runTask = task(() {// 离线消息 消息推送给移动端
    //  publishUnreceivedMessageRES(req);
    // });
    // taskPool.put(runTask);

     //离线消息 消息推送给移动端
    //logInfo ("sdddddddddddddddddddddddddddddddddddddd");
  }

  //收到离线消息ack接口
  @MessageId(MESSAGE.CMD_MESSAGE_ACK)
  void unrecevieACK(TransportContext ctx, MessageBuffer buffer) {
    auto req = new MsgAck();
    buffer.data.fromProtobuf!MsgAck(req);
    OfflineService.processDelteOffline(req.fromUserId);
    logInfo("delete offline msg by user : %s",req.fromUserId);
  }

  //未读消息角标接口
  @MessageId(MESSAGE.CMD_RED_SPOT)
  void redSpot(TransportContext ctx, MessageBuffer buffer) {
    auto req = new RED_SPOT();
    buffer.data.fromProtobuf!RED_SPOT(req);
    UserRepository userRep = new  UserRepository;
    userRep.setUnread(to!int(req.clientId),req.count);
    logInfo("redSpot user : %s",req.clientId);
  }
}

